package redis

/**
 * @Title  redis操作包
 * @Description redis操作的封装
 * @Author yaoweixin
 * @Update 2020/11/20 10:09.
 */

import (
	"gitee.com/yaodae/server-lib-go/config"
	"gitee.com/yaodae/server-lib-go/utils"
	"github.com/go-redis/redis/v8"
	"time"
)

func Init(cfg *config.RedisConfig) bool {
	if cfg == nil {
		return true
	}
	list := make([]RServerConfig, 0)
	for _, dbCfg := range cfg.RedisDb {
		serverConfig := RServerConfig{Name: dbCfg.RedisName, Ip: dbCfg.RedisUrl, Password: dbCfg.RedisPwd, Db: dbCfg.RedisDb}
		list = append(list, serverConfig)
	}
	redisConfig := RConfig{MinIdle: cfg.RedisMaxIdle, MaxActive: cfg.RedisMaxActive, IdleTimeout: cfg.RedisTimeout, Configs: list}
	initRedis(redisConfig)
	return true
}

type RUtil struct {
}

var Util = &RUtil{}

func PubSubConn(db string) *redis.Client {
	return getConn(db)
}

func toInterfaceList(value ...string) []interface{} {
	var args = make([]interface{}, 0)
	for i := range value {
		args = append(args, value[i])
	}
	return args
}

func expireTime(expire int) time.Duration {
	return time.Duration(expire) * time.Second
}

func (util *RUtil) doExpire(conn *redis.Client, key string, expire int) {
	_, _ = conn.Expire(ctx, key, expireTime(expire)).Result()
}

func (util *RUtil) del(db string, key string) {
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.Del(ctx, key).Result()
}

func (util *RUtil) exists(db string, key string) bool {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.Exists(ctx, key).Result()
	return rev == 1
}

func (util *RUtil) expire(db string, key string, expire int) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doExpire(conn, key, expire)
}

func (util *RUtil) get(db string, key string) string {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, err := conn.Get(ctx, key).Result()
	if err == redis.Nil {
		return ""
	}
	return rev
}

func (util *RUtil) getAny(db string, keys ...string) []string {
	values := make([]string, 0)
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.MGet(ctx, keys...).Result()
	if rev == nil {
		return values
	} else {
		for i := range rev {
			if rev[i] != nil {
				if _, ok := rev[i].(string); ok {
					values = append(values, rev[i].(string))
				} else if _, ok := rev[i].([]byte); ok {
					values = append(values, utils.ByteToStr(rev[i]))
				}
			} else {
				values = append(values, "")
			}
		}
		return values
	}
}

func (util *RUtil) set(db string, key string, value string) {
	conn := getConn(db)
	defer releaseConn(conn)
	conn.Set(ctx, key, value, 0)
}

func (util *RUtil) setEX(db string, key string, value string, expire int) {
	conn := getConn(db)
	defer releaseConn(conn)
	conn.Set(ctx, key, value, expireTime(expire))
}

func (util *RUtil) setNX(db string, key string, value string) bool {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.SetNX(ctx, key, value, expireTime(0)).Result()
	return rev
}

func (util *RUtil) setNxAndEx(db string, key string, value string, expire int) bool {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.SetNX(ctx, key, value, expireTime(expire)).Result()
	return rev
}

func (util *RUtil) incr(db string, key string) {
	util.incrBy(db, key, 1)
}

func (util *RUtil) incrBy(db string, key string, num int) {
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.IncrBy(ctx, key, int64(num)).Result()
}

func (util *RUtil) incrByValue(db string, key string, num int) int64 {
	conn := getConn(db)
	defer releaseConn(conn)
	value, _ := conn.IncrBy(ctx, key, int64(num)).Result()
	return value
}

func (util *RUtil) hDel(db string, key string, field string) {
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.HDel(ctx, key, field).Result()
}

func (util *RUtil) hExists(db string, key string, field string) bool {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.HExists(ctx, key, field).Result()
	return rev
}

func (util *RUtil) hGet(db string, key string, field string) string {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.HGet(ctx, key, field).Result()
	return rev
}

func (util *RUtil) doHSet(conn *redis.Client, key string, field string, value string) {
	_, _ = conn.HSet(ctx, key, field, value).Result()
}

func (util *RUtil) hSet(db string, key string, field string, value string) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doHSet(conn, key, field, value)
}

func (util *RUtil) hSetEx(db string, key string, field string, value string, expire int) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doHSet(conn, key, field, value)
	util.doExpire(conn, key, expire)
}

func (util *RUtil) hSetNX(db string, key string, field string, value string) bool {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.HSetNX(ctx, key, field, value).Result()
	return rev
}

func (util *RUtil) hGetAll(db string, key string) map[string]string {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.HGetAll(ctx, key).Result()
	return rev
}

func (util *RUtil) doHIncrBy(conn *redis.Client, key string, field string, num int) {
	_, _ = conn.HIncrBy(ctx, key, field, int64(num)).Result()
}

func (util *RUtil) hIncrBy(db string, key string, field string, num int) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doHIncrBy(conn, key, field, num)
}

func (util *RUtil) hIncrByEx(db string, key string, field string, num int, expire int) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doHIncrBy(conn, key, field, num)
	util.doExpire(conn, key, expire)
}

func (util *RUtil) hLen(db string, key string) int64 {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.HLen(ctx, key).Result()
	return rev
}

func (util *RUtil) hMGet(db string, key string, field ...string) []string {
	value := make([]string, 0)
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.HMGet(ctx, key, field...).Result()
	if rev == nil {
		return nil
	} else {
		for i := range rev {
			if rev[i] != nil {
				if _, ok := rev[i].(string); ok {
					value = append(value, rev[i].(string))
				} else if _, ok := rev[i].([]byte); ok {
					value = append(value, utils.ByteToStr(rev[i]))
				}
			}
		}
		return value
	}
}

func (util *RUtil) doHMSet(conn *redis.Client, key string, value ...string) {
	var args = toInterfaceList(value...)
	_, _ = conn.HMSet(ctx, key, args...).Result()
}

func (util *RUtil) hMSet(db string, key string, fieldValue ...string) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doHMSet(conn, key, fieldValue...)
}

func (util *RUtil) hMSetEx(db string, key string, expire int, fieldValue ...string) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doHMSet(conn, key, fieldValue...)
	_, _ = conn.Expire(ctx, key, expireTime(expire)).Result()
}

func (util *RUtil) hValues(db string, key string) []string {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.HVals(ctx, key).Result()
	return rev
}

func (util *RUtil) lLen(db string, key string) int64 {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.LLen(ctx, key).Result()
	return rev
}

func (util *RUtil) lLPop(db string, key string) string {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.LPop(ctx, key).Result()
	return rev
}

func (util *RUtil) lLPush(db string, key string, value ...string) {
	var args = toInterfaceList(value...)
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.LPush(ctx, key, args...).Result()
}

func (util *RUtil) lRPop(db string, key string) string {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.RPop(ctx, key).Result()
	return rev
}

func (util *RUtil) lRPush(db string, key string, value ...string) {
	var args = toInterfaceList(value...)
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.RPush(ctx, key, args...).Result()
}

func (util *RUtil) lRange(db string, key string, start, end int) []string {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.LRange(ctx, key, int64(start), int64(end)).Result()
	return rev
}

func (util *RUtil) doSAdd(conn *redis.Client, key string, value ...string) {
	var args = toInterfaceList(value...)
	_, _ = conn.SAdd(ctx, key, args...).Result()
}

func (util *RUtil) sAdd(db string, key string, value ...string) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doSAdd(conn, key, value...)
}

func (util *RUtil) sAddEx(db string, key string, expire int, value ...string) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doSAdd(conn, key, value...)
	util.doExpire(conn, key, expire)
}

func (util *RUtil) sCard(db string, key string) int64 {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.SCard(ctx, key).Result()
	return rev
}

func (util *RUtil) sRem(db string, key string, value ...string) {
	var args = toInterfaceList(value...)
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.SRem(ctx, key, args...).Result()
}

func (util *RUtil) sMembers(db string, key string) []string {
	var args = make([]string, 0)
	conn := getConn(db)
	defer releaseConn(conn)
	var cursor uint64
	var n int
	for {
		var keys []string
		var err error
		keys, cursor, err = conn.SScan(ctx, key, cursor, "", 100).Result()
		if err != nil {
			keys = []string{}
		}
		n += len(keys)
		args = append(args, keys...)
		if cursor == 0 {
			break
		}
	}
	return args
}

/**
redis.RZAdd("default", "test_set", "test1", 1.1)
*/
func (util *RUtil) doZAdd(conn *redis.Client, key string, member string, score float64) {
	_, _ = conn.ZAdd(ctx, key, &redis.Z{Score: score, Member: member}).Result()
}

/**
redis.RZAdd("default", "test_set", "test1", 1.1)
*/
func (util *RUtil) zAdd(db string, key string, member string, score float64) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doZAdd(conn, key, member, score)
}

func (util *RUtil) zAddEx(db string, key string, member string, score float64, expire int) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doZAdd(conn, key, member, score)
	util.doExpire(conn, key, expire)
}

/**
redis.RZAdd("default", "test_set", "test1", 1.1)
*/
func (util *RUtil) doZAdds(conn *redis.Client, key string, arg ...interface{}) {
	var args = make([]*redis.Z, 0)
	for i := range arg {
		args = append(args, &redis.Z{Score: arg[i].(float64), Member: arg[i+1]})
	}
	_, _ = conn.ZAdd(ctx, key, args...).Result()
}

/**
redis.RZAdds("default", "test_set",1.2,  "test2", 1.3, "test3")
*/
func (util *RUtil) zAdds(db string, key string, arg ...interface{}) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doZAdds(conn, key, arg...)
}

func (util *RUtil) zAddsEx(db string, key string, expire int, arg ...interface{}) {
	conn := getConn(db)
	defer releaseConn(conn)
	util.doZAdds(conn, key, arg...)
	util.doExpire(conn, key, expire)
}

func (util *RUtil) zCard(db string, key string) int64 {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, _ := conn.ZCard(ctx, key).Result()
	return rev
}

func (util *RUtil) zIncrBy(db string, key string, member string, score float64) {
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.ZIncrBy(ctx, key, score, member).Result()
}

func (util *RUtil) zRange(db string, key string, start int, end int, withScore bool, isRev bool) [][]string {
	value := make([][]string, 0)
	conn := getConn(db)
	defer releaseConn(conn)
	var scoreRev []redis.Z
	var valueRev []string
	if isRev && withScore {
		scoreRev, _ = conn.ZRevRangeWithScores(ctx, key, int64(start), int64(end)).Result()
	} else if isRev {
		valueRev, _ = conn.ZRevRange(ctx, key, int64(start), int64(end)).Result()
	} else if withScore {
		scoreRev, _ = conn.ZRangeWithScores(ctx, key, int64(start), int64(end)).Result()
	} else {
		valueRev, _ = conn.ZRange(ctx, key, int64(start), int64(end)).Result()
	}

	if scoreRev == nil && valueRev == nil {
		return nil
	} else {
		if withScore && scoreRev != nil {
			num := len(scoreRev)
			for i := 0; i < num; i++ {
				k := utils.InterfaceToStr(scoreRev[i].Member)
				v := utils.FloatToStr(scoreRev[i].Score)
				value = append(value, []string{k, v})
			}
		} else if valueRev != nil {
			num := len(valueRev)
			for i := 0; i < num; i++ {
				value = append(value, []string{valueRev[i]})
			}
		}
		return value
	}
}

func (util *RUtil) zRangeByScore(db string, key string, min float64, max float64, withScore bool, isRev bool) interface{} {
	return util.zRangeByScoreLimit(db, key, min, max, withScore, isRev, -1, -1)
}

func (util *RUtil) zRangeByScoreLimit(db string, key string, min float64, max float64, withScore bool, isRev bool, offset int, count int) [][]string {
	value := make([][]string, 0)
	conn := getConn(db)
	defer releaseConn(conn)
	var scoreRev []redis.Z
	var valueRev []string
	var opt = &redis.ZRangeBy{}
	if offset >= 0 && count > 0 {
		opt.Offset = int64(offset)
		opt.Count = int64(count)
	}
	if isRev {
		opt.Min = utils.FloatToStr(max)
		opt.Max = utils.FloatToStr(min)
	} else {
		opt.Min = utils.FloatToStr(min)
		opt.Max = utils.FloatToStr(max)
	}
	if isRev && withScore {
		scoreRev, _ = conn.ZRevRangeByScoreWithScores(ctx, key, opt).Result()
	} else if isRev {
		valueRev, _ = conn.ZRevRangeByScore(ctx, key, opt).Result()
	} else if withScore {
		scoreRev, _ = conn.ZRangeByScoreWithScores(ctx, key, opt).Result()
	} else {
		valueRev, _ = conn.ZRangeByScore(ctx, key, opt).Result()
	}
	if scoreRev == nil && valueRev == nil {
		return nil
	} else {
		if withScore && scoreRev != nil {
			num := len(scoreRev)
			for i := 0; i < num; i++ {
				k := utils.InterfaceToStr(scoreRev[i].Member)
				v := utils.FloatToStr(scoreRev[i].Score)
				value = append(value, []string{k, v})
			}
		} else if valueRev != nil {
			num := len(valueRev)
			for i := 0; i < num; i++ {
				value = append(value, []string{valueRev[i]})
			}
		}
		return value
	}
}

/**
0为开始
*/
func (util *RUtil) zRank(db string, key string, member string, isRev bool) (int64, error) {
	conn := getConn(db)
	defer releaseConn(conn)
	if isRev {
		rev, err := conn.ZRevRank(ctx, key, member).Result()
		return rev, err
	} else {
		rev, err := conn.ZRank(ctx, key, member).Result()
		return rev, err
	}
}

func (util *RUtil) zRem(db string, key string, member ...string) {
	var args = toInterfaceList(member...)
	conn := getConn(db)
	defer releaseConn(conn)
	_, _ = conn.ZRem(ctx, key, args...).Result()
}

func (util *RUtil) zRemByRank(db string, key string, start, stop int64) (int64, error) {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, err := conn.ZRemRangeByRank(ctx, key, start, stop).Result()
	return rev, err
}

func (util *RUtil) zScore(db string, key string, member string) (float64, error) {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, err := conn.ZScore(ctx, key, member).Result()
	return rev, err
}

func (util *RUtil) eval(db string, script string, keys []string, args ...interface{}) (interface{}, error) {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, err := conn.Eval(ctx, script, keys, args...).Result()
	return rev, err
}

func (util *RUtil) ttl(db string, key string) (time.Duration, error) {
	conn := getConn(db)
	defer releaseConn(conn)
	rev, err := conn.TTL(ctx, key).Result()
	return rev, err
}
